package com.onlyxiahui.common.utils.base.compare;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import com.onlyxiahui.common.utils.base.lang.reflect.ReflectUtil;

/**
 * 
 * Description <br>
 * Date 2020-11-10 14:47:42<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */
public class EqualsUtil {

	/**
	 * 
	 * Description 2个对象简单比较<br>
	 * Date 2020-11-10 14:45:20<br>
	 * 
	 * @param source
	 * @param target
	 * @return boolean
	 * @since 1.0.0
	 */
	@SuppressWarnings("unchecked")
	public static boolean isEquals(Object source, Object target) {
		boolean isEquals = false;
		if (null != source && null != target) {

			isEquals = (source == target);
			if (!isEquals) {
				isEquals = source.equals(target);
			}

			if (!isEquals) {
				isEquals = Objects.deepEquals(source, target);
			}

			if (!isEquals) {
				boolean mark = (source instanceof Comparable && target instanceof Comparable) && source.getClass() == target.getClass();
				if (mark) {
					isEquals = ((Comparable<Object>) source).compareTo(target) == 0;
				}
			}
		} else if (null == source && null == target) {
			// 2个值都不存在认为相等
			isEquals = true;
		} else {
			// 2个值其中一个不存在认为不相等
			isEquals = false;
		}
		return isEquals;
	}

	/**
	 * 
	 * Description 2个属性值判断是否相等<br>
	 * 1、当2个属性的值或者属性都为null->true<br>
	 * 2、当2个属性的值或者属性都不为null并且相等->true<br>
	 * 3、当2个属性的值或者属性都不为null并且不相等->false<br>
	 * 4、当2个属性的值或者属性都其中一个为null->false<br>
	 * 
	 * Date 2020-11-10 14:44:47<br>
	 * 
	 * @param source
	 * @param sourceField
	 * @param target
	 * @param targetField
	 * @return
	 * @since 1.0.0
	 */
	public static boolean isPropertyEquals(Object source, Field sourceField, Object target, Field targetField) {
		boolean isEquals = false;

		Object sourceValue = null;
		Object targetValue = null;

		if (null != source && null != sourceField) {
			sourceValue = ReflectUtil.getFieldValue(source, sourceField);
		}
		if (null != target && null != targetField) {
			targetValue = ReflectUtil.getFieldValue(target, targetField);
		}
		isEquals = isEquals(sourceValue, targetValue);
		return isEquals;
	}

	/**
	 * Description 按指定的属性列表，2个对象进行值比对，适用于简单的对象<br>
	 * 这个方法适用于2个对象的属性名并不完全一致，其中Map<String, String> propertyMap参数是用于2个对象相应属性名进行映射<br>
	 * <br>
	 * Date 2020-11-10 14:46:43<br>
	 * 
	 * @param <T>
	 * @param <E>
	 * @param propertyMap
	 * @param source          ：需要比较的源对象<br>
	 * @param sourceRootClass :源对象往上搜索字段的最顶层父类（默认Object）<br>
	 * @param target          :需要比较的目标对象<br>
	 * @param targetRootClass :目标对象往上搜索字段的最顶层父类（默认Object）<br>
	 * @return
	 * @since 1.0.0
	 */
	public static <T, E> boolean isPropertyMapEquals(Map<String, String> propertyMap, T source, Class<?> sourceRootClass, E target, Class<?> targetRootClass) {
		boolean isEquals = true;

		// 各参数都不能为空
		if (null != source && null != target) {

			sourceRootClass = null == sourceRootClass ? Object.class : sourceRootClass;
			targetRootClass = null == targetRootClass ? Object.class : targetRootClass;

			Class<?> sourceClass = source.getClass();
			Class<?> targetClass = target.getClass();

			if (null == propertyMap) {
				propertyMap = new HashMap<>();
				List<Field> list = ReflectUtil.getFieldList(sourceClass, sourceRootClass);
				for (Field f : list) {
					propertyMap.put(f.getName(), f.getName());
				}
			}

			Set<String> keySet = propertyMap.keySet();
			for (String sourceKey : keySet) {
				// 取出需要对比的属性名
				String targetKey = propertyMap.get(sourceKey);

				Field sourceField = ReflectUtil.getField(sourceClass, sourceRootClass, sourceKey);
				Field targetField = ReflectUtil.getField(targetClass, targetRootClass, targetKey);
				if (!isPropertyEquals(source, sourceField, target, targetField)) {
					isEquals = false;
					break;
				}
			}
		}
		return isEquals;
	}

	/**
	 * Description 按指定的属性列表，2个对象进行值比对，适用于简单的对象<br>
	 * 这个方法适用于2个对象的属性名并不完全一致，其中Map<String, String> propertyMap参数是用于2个对象相应属性名进行映射<br>
	 * 
	 * Map<String, String> propertyMap = new HashMap<>();<br>
	 * propertyMap.put("id","userId");<br>
	 * propertyMap.put("name","userName");<br>
	 * 
	 * Object source = new Object();<br>
	 * Object target = new Object();<br>
	 * boolean isEquals = EqualsUtil.isPropertyEquals(propertyMap, source,
	 * target)<br>
	 * 
	 * Date 2020-11-10 14:49:21<br>
	 * 
	 * @param <T>
	 * @param <E>
	 * @param propertyMap
	 * @param source
	 * @param target
	 * @return
	 * @since 1.0.0
	 */
	public static <T, E> boolean isPropertyMapEquals(Map<String, String> propertyMap, T source, E target) {
		return isPropertyMapEquals(propertyMap, source, null, target, null);
	}

	/**
	 * 
	 * Description Description 2个对象属性值判断是否相等<br>
	 * Date 2020-11-10 15:56:26<br>
	 * 
	 * @param <T>
	 * @param <E>
	 * @param source
	 * @param target
	 * @return
	 * @since 1.0.0
	 */
	public static <T, E> boolean isPropertyEquals(T source, E target) {
		return isPropertyMapEquals((Map<String, String>) null, source, null, target, null);
	}

	/**
	 * Description 按指定的属性列表，2个对象属性值进行比对，适用于简单属性的对象<br>
	 * List<String> propertyList = new ArrayList<>(); <br>
	 * propertyList.add("name");<br>
	 * propertyList.add("code");<br>
	 * propertyList.add("departmentCodes");<br>
	 * 
	 * Object source = new Object();<br>
	 * Object target = new Object();<br>
	 * boolean isEquals = EqualsUtil.isPropertyEquals(propertyList, source,
	 * target)<br>
	 * 
	 * 
	 * Date 2020-11-10 14:55:38<br>
	 * 
	 * @param <T>
	 * @param <E>
	 * @param propertyList
	 * @param source
	 * @param target
	 * @return
	 * @since 1.0.0
	 */
	public static <T, E> boolean isPropertyListEquals(List<String> propertyList, T source, E target) {
		return isPropertyListEquals(propertyList, source, null, target, null);
	}

	/**
	 * Description 按指定的属性列表，2个对象属性值进行比对，适用于简单属性的对象<br>
	 * List<String> propertyList = new ArrayList<>(); <br>
	 * propertyList.add("name");<br>
	 * propertyList.add("code");<br>
	 * propertyList.add("departmentCodes");<br>
	 * 
	 * Object source = new Object();<br>
	 * Object target = new Object();<br>
	 * boolean isEquals = EqualsUtil.isPropertyEquals(propertyList, source, null
	 * target, null )<br>
	 * 
	 * 
	 * Date 2020-11-10 14:56:47<br>
	 * 
	 * @param <T>
	 * @param <E>
	 * @param propertyList
	 * @param source
	 * @param sourceRootClass
	 * @param target
	 * @param targetRootClass
	 * @return
	 * @since 1.0.0
	 */
	public static <T, E> boolean isPropertyListEquals(List<String> propertyList, T source, Class<?> sourceRootClass, E target, Class<?> targetRootClass) {
		Map<String, String> map = null;
		if (null != propertyList) {
			map = new HashMap<>(propertyList.size());
			for (String name : propertyList) {
				map.put(name, name);
			}
		}
		boolean isEquals = isPropertyMapEquals(map, source, sourceRootClass, target, targetRootClass);
		return isEquals;
	}
}
